class_name Player
extends RigidBody2D

# Character Demo, written by Juan Linietsky.
#
#  Implementation of a 2D Character controller.
#  This implementation uses the physics engine for
#  controlling a character, in a very similar way
#  than a 3D character controller would be implemented.
#
#  Using the physics engine for this has the main advantages:
#    - Easy to write.
#    - Interaction with other physics-based objects is free
#    - Only have to deal with the object linear velocity, not position
#    - All collision/area framework available
#
#  But also has the following disadvantages:
#    - Objects may bounce a little bit sometimes
#    - Going up ramps sends the chracter flying up, small hack is needed.
#    - A ray collider is needed to avoid sliding down on ramps and
#      undesiderd bumps, small steps and rare numerical precision errors.
#      (another alternative may be to turn on friction when the character is not moving).
#    - Friction cant be used, so floor velocity must be considered
#      for moving platforms.
#角色演示，由胡安·利涅茨基撰写。
#
#二维字符控制器的实现。
#此实现使用物理引擎
#以非常相似的方式控制角色
#而不是一个3D角色控制器。
#
#为此使用物理引擎有以下主要优点：
#-易于书写。
#-与其他物理对象的交互是免费的
#-只需处理物体的线速度，而不是位置
#-所有碰撞/区域框架可用
#
#但也有以下缺点：
#-物体有时会反弹一点
#-上斜坡会让怪物飞起来，需要小的攻击。
#-需要一台射线对撞机，以避免在坡道上滑下
#意外的颠簸，小的步骤和罕见的数值精度误差。
#（另一种选择可能是在角色不移动时启用“摩擦力”）。
#-不能使用摩擦，因此必须考虑地板速度
#用于移动平台。
const WALK_ACCEL = 500.0
const WALK_DEACCEL = 500.0
const WALK_MAX_VELOCITY = 140.0
const AIR_ACCEL = 100.0
const AIR_DEACCEL = 100.0
const JUMP_VELOCITY = 380
const STOP_JUMP_FORCE = 450.0
const MAX_SHOOT_POSE_TIME = 0.3
const MAX_FLOOR_AIRBORNE_TIME = 0.15

var anim = ""
var siding_left = false
var jumping = false
var stopping_jump = false
var shooting = false

var floor_h_velocity = 0.0
#腾空时间
var airborne_time = 1e20
var shoot_time = 1e20

var Bullet = preload("res://player/Bullet.tscn")
var Enemy = preload("res://enemy/Enemy.tscn")

func _integrate_forces(s):
	#s是Physics2DDirectBodyState
	#直接访问Physics2DServer(物理2D服务)中的物理实体的对象

#	print_debug(s)
	
	#得到物体的线速度.
	var lv = s.get_linear_velocity()
	#print_debug(lv)
	#用于仿真的时间步长(增量)
	var step = s.get_step()
	#print_debug(step)
	#动画
	var new_anim = anim
	#是否侧边左
	var new_siding_left = siding_left
	
	#得到输入控制
	# Get the controls.
	var move_left = Input.is_action_pressed("move_left")
	var move_right = Input.is_action_pressed("move_right")
	var jump = Input.is_action_pressed("jump")
	var shoot = Input.is_action_pressed("shoot")
	var spawn = Input.is_action_pressed("spawn")
	#生成怪物 F1
	if spawn:
		call_deferred("_spawn_enemy_above")
	
	#得到物体的线速度.
	#取消/预防上一层速度。？
	# Deapply prev floor velocity.
	lv.x -= floor_h_velocity
	floor_h_velocity = 0.0
	
	#找到floor地面
	# Find the floor (a contact with upwards facing collision normal).
	var found_floor = false
	var floor_index = -1
	#x是index s是Physics2DDirectBodyState 返回该机构与其他机构的联系次数.

	for x in range(s.get_contact_count()):
		#返回接触点的局部法线.

		var ci = s.get_contact_local_normal(x)
		
		if ci.dot(Vector2(0, -1)) > 0.6:
			found_floor = true
			floor_index = x
	
	# A good idea when implementing characters of all kinds,
	# compensates for physics imprecision, as well as human reaction delay.
	if shoot and not shooting:
		call_deferred("_shot_bullet")
	else:
		shoot_time += step
	#如果知道地面 空气时间为0 否则 空气时间+
	if found_floor:
		airborne_time = 0.0
	else:
		airborne_time += step # Time it spent in the air. 在空气中花的时间
	#最大地面空气时间0.15
	var on_floor = airborne_time < MAX_FLOOR_AIRBORNE_TIME
	#print_debug(on_floor)
	# Process jump.
	#如果正在跳跃
	if jumping:
		#如果得到物体的线速度的y>0
		if lv.y > 0:
			#如果下降了 关闭跳跃状态
			# Set off the jumping flag if going down.
			jumping = false
		#否则 如果没有按下跳跃 停止跳跃
		elif not jump:
			stopping_jump = true
		#如果停止跳跃 得到物体的线速度的y+=停止跳跃力450.0*用于仿真的时间步长(增量)（delta)
		if stopping_jump:
			lv.y += STOP_JUMP_FORCE * step
	
	if on_floor:
		# Process logic when character is on floor.
		if move_left and not move_right:
			if lv.x > -WALK_MAX_VELOCITY:
				lv.x -= WALK_ACCEL * step
		elif move_right and not move_left:
			if lv.x < WALK_MAX_VELOCITY:
				lv.x += WALK_ACCEL * step
		else:
			var xv = abs(lv.x)
			xv -= WALK_DEACCEL * step
			if xv < 0:
				xv = 0
			lv.x = sign(lv.x) * xv
		
		# Check jump.
		if not jumping and jump:
			lv.y = -JUMP_VELOCITY
			jumping = true
			stopping_jump = false
			($SoundJump as AudioStreamPlayer2D).play()
		
		# Check siding.
		if lv.x < 0 and move_left:
			new_siding_left = true
		elif lv.x > 0 and move_right:
			new_siding_left = false
		if jumping:
			new_anim = "jumping"
		elif abs(lv.x) < 0.1:
			if shoot_time < MAX_SHOOT_POSE_TIME:
				new_anim = "idle_weapon"
			else:
				new_anim = "idle"
		else:
			if shoot_time < MAX_SHOOT_POSE_TIME:
				new_anim = "run_weapon"
			else:
				new_anim = "run"
	else:
		# Process logic when the character is in the air.
		if move_left and not move_right:
			if lv.x > -WALK_MAX_VELOCITY:
				lv.x -= AIR_ACCEL * step
		elif move_right and not move_left:
			if lv.x < WALK_MAX_VELOCITY:
				lv.x += AIR_ACCEL * step
		else:
			var xv = abs(lv.x)
			xv -= AIR_DEACCEL * step
			
			if xv < 0:
				xv = 0
			lv.x = sign(lv.x) * xv
		
		if lv.y < 0:
			if shoot_time < MAX_SHOOT_POSE_TIME:
				new_anim = "jumping_weapon"
			else:
				new_anim = "jumping"
		else:
			if shoot_time < MAX_SHOOT_POSE_TIME:
				new_anim = "falling_weapon"
			else:
				new_anim = "falling"
	
	# Update siding.
	if new_siding_left != siding_left:
		if new_siding_left:
			($Sprite as Sprite).scale.x = -1
		else:
			($Sprite as Sprite).scale.x = 1
		
		siding_left = new_siding_left
	
	# Change animation.
	if new_anim != anim:
		anim = new_anim
		($AnimationPlayer as AnimationPlayer).play(anim)
	
	shooting = shoot
	#应用地面速度
	# Apply floor velocity.
	#如果找到地面 地面横向速度=Physics2DDirectBodyState.获得在特定位置碰到的碰撞体
	#(地面index-1.x
	#返回对撞机接触点处的线速度向量.

	if found_floor:
		floor_h_velocity = s.get_contact_collider_velocity_at_position(floor_index).x
		#print_debug(floor_h_velocity)
		lv.x += floor_h_velocity
	#最后 应用中立 并且设置 线形速度 
	# Finally, apply gravity and set back the linear velocity.
	#得到当前应用于这个物体的总重力向量 *delta
	lv += s.get_total_gravity() * step
	#关键代码
	#print(s.get_total_gravity()*step)
	#Physics2DDirectBodyState. 设置线形速度 物体的线速度.
	s.set_linear_velocity(lv)
	#print(lv)
	#通过设置线形速度 直接控制物理现象
#	s.set_linear_velocity(Vector2(100,0))
	#设置物体的线形速度

func _shot_bullet():
	shoot_time = 0
	var bi = Bullet.instance()
	var ss
	if siding_left:
		ss = -1.0
	else:
		ss = 1.0
	var pos = position + ($BulletShoot as Position2D).position * Vector2(ss, 1.0)
		
	bi.position = pos
	get_parent().add_child(bi)
	
	bi.linear_velocity = Vector2(400.0 * ss, -40)
	
	($Sprite/Smoke as Particles2D).restart()
	($SoundShoot as AudioStreamPlayer2D).play()
	
	add_collision_exception_with(bi) # Make bullet and this not collide.


func _spawn_enemy_above():
	var e = Enemy.instance()
	e.position = position + 50 * Vector2.UP
	get_parent().add_child(e)
